{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "421aeefd-c8c3-4845-b17a-c4cf2381d949",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T15:52:27.768265Z",
     "iopub.status.busy": "2022-06-21T15:52:27.767673Z",
     "iopub.status.idle": "2022-06-21T15:52:27.773696Z",
     "shell.execute_reply": "2022-06-21T15:52:27.772571Z",
     "shell.execute_reply.started": "2022-06-21T15:52:27.768214Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import torch\n",
    "from sklearn.preprocessing import LabelEncoder\n",
    "from sklearn.metrics import roc_auc_score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "7738adda-c09a-480d-96d3-539ff6a87f4a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T15:54:48.329686Z",
     "iopub.status.busy": "2022-06-21T15:54:48.329084Z",
     "iopub.status.idle": "2022-06-21T15:54:49.083733Z",
     "shell.execute_reply": "2022-06-21T15:54:49.083181Z",
     "shell.execute_reply.started": "2022-06-21T15:54:48.329617Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "train_df = pd.read_parquet(\"train_df.parquet\")\n",
    "test_df = pd.read_parquet(\"test_df.parquet\")\n",
    "\n",
    "train_data = np.load(\"train_data.npy\")\n",
    "test_data = np.load(\"test_data.npy\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "ab91a925-80c9-4df7-bd5d-6a6a9ac9bf3b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T15:54:49.765081Z",
     "iopub.status.busy": "2022-06-21T15:54:49.764479Z",
     "iopub.status.idle": "2022-06-21T15:54:54.357463Z",
     "shell.execute_reply": "2022-06-21T15:54:54.356963Z",
     "shell.execute_reply.started": "2022-06-21T15:54:49.765029Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "for col in [\"pkgname\", \"ver\", \"slotid\", \"mediaid\", \"material\"]:\n",
    "    train_df[col] = train_df[col].str.replace(\"b'\", \"\").str.replace(\"'\", \"\")\n",
    "    test_df[col] = test_df[col].str.replace(\"b'\", \"\").str.replace(\"'\", \"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "d1f914f7-832f-4da5-91bd-70bd38249c07",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T15:54:56.129653Z",
     "iopub.status.busy": "2022-06-21T15:54:56.129049Z",
     "iopub.status.idle": "2022-06-21T15:55:06.041545Z",
     "shell.execute_reply": "2022-06-21T15:55:06.041046Z",
     "shell.execute_reply.started": "2022-06-21T15:54:56.129601Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "for col in [\"pkgname\", \"ver\", \"slotid\", \"mediaid\", \"material\"]:\n",
    "    lbl = LabelEncoder()\n",
    "    lbl.fit(train_df[col].tolist() + test_df[col].tolist())\n",
    "\n",
    "    train_df[col] = lbl.transform(train_df[col])\n",
    "    test_df[col] = lbl.transform(test_df[col])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "f545f71e-0481-452b-8de7-950b38d0b8d8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T15:56:14.086608Z",
     "iopub.status.busy": "2022-06-21T15:56:14.086004Z",
     "iopub.status.idle": "2022-06-21T15:56:14.110240Z",
     "shell.execute_reply": "2022-06-21T15:56:14.109721Z",
     "shell.execute_reply.started": "2022-06-21T15:56:14.086558Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pkgname 262 262\n",
      "ver 4014 4013\n",
      "slotid 5999 5999\n",
      "mediaid 101 101\n",
      "material 277 299\n"
     ]
    }
   ],
   "source": [
    "for col in [\"pkgname\", \"ver\", \"slotid\", \"mediaid\", \"material\"]:\n",
    "    print(col, train_df[col].max(), test_df[col].max())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "11b2585f-14b2-4129-bcf7-8d07673b27ab",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T15:56:58.767812Z",
     "iopub.status.busy": "2022-06-21T15:56:58.767206Z",
     "iopub.status.idle": "2022-06-21T15:56:58.783566Z",
     "shell.execute_reply": "2022-06-21T15:56:58.783101Z",
     "shell.execute_reply.started": "2022-06-21T15:56:58.767763Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0              2199327\n",
       "1                20210\n",
       "adx_slot_id          0\n",
       "Name: label, dtype: int64"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_df[\"label\"].value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 326,
   "id": "18d4d45b-2fd3-4042-9508-e1a8e7b1872f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:17:49.282949Z",
     "iopub.status.busy": "2022-06-21T17:17:49.282300Z",
     "iopub.status.idle": "2022-06-21T17:17:49.288845Z",
     "shell.execute_reply": "2022-06-21T17:17:49.288303Z",
     "shell.execute_reply.started": "2022-06-21T17:17:49.282899Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2219537, 240)"
      ]
     },
     "execution_count": 326,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_data.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 374,
   "id": "d02ded74-ef0d-40de-950d-efa8b0efe547",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:40:39.869237Z",
     "iopub.status.busy": "2022-06-21T17:40:39.868651Z",
     "iopub.status.idle": "2022-06-21T17:40:39.882413Z",
     "shell.execute_reply": "2022-06-21T17:40:39.881725Z",
     "shell.execute_reply.started": "2022-06-21T17:40:39.869188Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn.functional as F\n",
    "from torch import nn\n",
    "from torch.autograd import Variable\n",
    "from torch.utils.data import DataLoader, Dataset\n",
    "from torchmetrics.functional import auc, auroc\n",
    "torch.manual_seed(0)\n",
    "\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "\n",
    "class MLP(nn.Module):\n",
    "    def __init__(self, category_dict, layers=[45+240, 32], dropout=False):\n",
    "        super().__init__()\n",
    "        print(category_dict)\n",
    "        self.category_dict = category_dict\n",
    "        self.embedding_dict = {\n",
    "            key: torch.nn.Embedding(\n",
    "                self.category_dict[key]+1, int(np.log2(self.category_dict[key]))\n",
    "            ).to(device)\n",
    "            for key in category_dict.keys()\n",
    "        }\n",
    "\n",
    "        self.fc_layers = torch.nn.ModuleList()\n",
    "        for _, (in_size, out_size) in enumerate(zip(layers[:-1], layers[1:])):\n",
    "            self.fc_layers.append(torch.nn.Linear(in_size, out_size).to(device))\n",
    "        self.output_layer = torch.nn.Linear(layers[-1], 1).to(device)\n",
    "\n",
    "    def forward(self, feed_dict, embed_dict):\n",
    "        embedding_feet = {\n",
    "            key: self.embedding_dict[key](feed_dict[key])\n",
    "            for key in self.category_dict.keys()\n",
    "        }\n",
    "\n",
    "        x = torch.cat(list(embedding_feet.values()), 1)\n",
    "        x = torch.cat([x, embed_dict], 1)\n",
    "        for idx, _ in enumerate(range(len(self.fc_layers))):\n",
    "            x = self.fc_layers[idx](x)\n",
    "            x = F.relu(x)\n",
    "            x = F.dropout(x)\n",
    "        logit = self.output_layer(x)\n",
    "        return logit\n",
    "\n",
    "    def predict(self, feed_dict):\n",
    "        for key in feed_dict:\n",
    "            if type(feed_dict[key]) != type(None):\n",
    "                feed_dict[key] = torch.from_numpy(feed_dict[key]).to(\n",
    "                    dtype=torch.long, device=\"cpu\"\n",
    "                )\n",
    "        output_scores = self.forward(feed_dict)\n",
    "        return output_scores"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 375,
   "id": "0ccc0a45-22f1-415a-bdf7-b5f436996ee9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:40:39.883579Z",
     "iopub.status.busy": "2022-06-21T17:40:39.883380Z",
     "iopub.status.idle": "2022-06-21T17:40:40.015838Z",
     "shell.execute_reply": "2022-06-21T17:40:40.015325Z",
     "shell.execute_reply.started": "2022-06-21T17:40:39.883562Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'pkgname': 263, 'ver': 4015, 'slotid': 6000, 'mediaid': 102, 'material': 300}\n"
     ]
    }
   ],
   "source": [
    "category_dict = {\n",
    "    \"pkgname\": 263,\n",
    "    \"ver\": 4015,\n",
    "    \"slotid\": 6000,\n",
    "    \"mediaid\": 102,\n",
    "    \"material\": 300,\n",
    "}\n",
    "\n",
    "model = MLP(category_dict)\n",
    "model.to(device)\n",
    "\n",
    "optimizer = torch.optim.AdamW(model.parameters(), lr=0.008)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 376,
   "id": "e92fe4a5-1966-4f20-b84b-5b38829f4fe1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:40:40.321915Z",
     "iopub.status.busy": "2022-06-21T17:40:40.321319Z",
     "iopub.status.idle": "2022-06-21T17:40:40.331182Z",
     "shell.execute_reply": "2022-06-21T17:40:40.330666Z",
     "shell.execute_reply.started": "2022-06-21T17:40:40.321866Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0.1038],\n",
       "        [0.2315]], device='cuda:0', grad_fn=<AddmmBackward>)"
      ]
     },
     "execution_count": 376,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example_cate_feat = {key: torch.tensor([0, 1]).long().to(device) for key in category_dict.keys()}\n",
    "model(example_cate_feat, torch.zeros(2, 240).to(device))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 377,
   "id": "292d2a48-99fb-4e39-b0df-a2f11e8890b0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:40:41.870190Z",
     "iopub.status.busy": "2022-06-21T17:40:41.869537Z",
     "iopub.status.idle": "2022-06-21T17:40:42.018293Z",
     "shell.execute_reply": "2022-06-21T17:40:42.017825Z",
     "shell.execute_reply.started": "2022-06-21T17:40:41.870139Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class MLPDataset(Dataset):\n",
    "    def __init__(self, cate_df, embed_feat, label=None, train=True):\n",
    "        self.cate_df = cate_df\n",
    "        self.embed_feat = embed_feat\n",
    "        self.label = label\n",
    "        self.train = train\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        cate_feat = {}\n",
    "        # for col in self.cate_df.columns:\n",
    "        #     cate_feat[col] = self.cate_df[col].iloc[index]\n",
    "        cate_feat = self.cate_df.iloc[index].to_dict()\n",
    "        \n",
    "        if self.train:\n",
    "            label = self.label[index]\n",
    "            return cate_feat, self.embed_feat[index],label\n",
    "        else:\n",
    "            return cate_feat, self.embed_feat[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.cate_df)\n",
    "\n",
    "train_loader = torch.utils.data.DataLoader(\n",
    "    MLPDataset(\n",
    "        train_df.iloc[:-500000, 1:],\n",
    "        train_data,\n",
    "        train_df[\"label\"].values[:-500000].astype(int),\n",
    "    ),\n",
    "    batch_size=3000,\n",
    "    shuffle=True,\n",
    "    num_workers=0,\n",
    ")\n",
    "\n",
    "val_loader = torch.utils.data.DataLoader(\n",
    "    MLPDataset(\n",
    "        train_df.iloc[-500000:, 1:],\n",
    "        train_data,\n",
    "        train_df[\"label\"].values[-500000:].astype(int),\n",
    "    ),\n",
    "    batch_size=5000,\n",
    "    shuffle=False,\n",
    "    num_workers=0,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 378,
   "id": "9c6da8a8-9e79-4c54-a83b-c5f9b07b02f3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:40:42.869313Z",
     "iopub.status.busy": "2022-06-21T17:40:42.868727Z",
     "iopub.status.idle": "2022-06-21T17:40:42.876670Z",
     "shell.execute_reply": "2022-06-21T17:40:42.876082Z",
     "shell.execute_reply.started": "2022-06-21T17:40:42.869263Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'label': '0',\n",
       " 'pkgname': 36,\n",
       " 'ver': 3438,\n",
       " 'slotid': 3048,\n",
       " 'mediaid': 88,\n",
       " 'material': 262}"
      ]
     },
     "execution_count": 378,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_df.iloc[10].to_dict()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 379,
   "id": "979f7ed2-96d8-4ef3-bf6e-09b9665cd873",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:40:43.866482Z",
     "iopub.status.busy": "2022-06-21T17:40:43.865897Z",
     "iopub.status.idle": "2022-06-21T17:40:44.189944Z",
     "shell.execute_reply": "2022-06-21T17:40:44.189430Z",
     "shell.execute_reply.started": "2022-06-21T17:40:43.866432Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 398 ms, sys: 4.06 ms, total: 402 ms\n",
      "Wall time: 319 ms\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "for _ in train_loader:\n",
    "    break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 380,
   "id": "86ff3a7c-0b2e-403f-8339-30604a81f9bd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:40:45.866327Z",
     "iopub.status.busy": "2022-06-21T17:40:45.865737Z",
     "iopub.status.idle": "2022-06-21T17:53:56.985478Z",
     "shell.execute_reply": "2022-06-21T17:53:56.984963Z",
     "shell.execute_reply.started": "2022-06-21T17:40:45.866278Z"
    },
    "scrolled": true,
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0/574\t 1.3225828409194946/0.533422589302063\n",
      "100/574\t 0.8862520456314087/0.3743702471256256\n",
      "200/574\t 0.9831544756889343/0.3598862290382385\n",
      "300/574\t 0.8013485670089722/0.653688371181488\n",
      "400/574\t 0.8437849879264832/0.784477710723877\n",
      "500/574\t 0.8376685976982117/0.7145994305610657\n",
      "0.7265792533080638\n",
      "0/574\t 0.634872555732727/0.7282460331916809\n",
      "100/574\t 0.7812550663948059/0.32703566551208496\n",
      "200/574\t 0.8246058821678162/0.703112781047821\n",
      "300/574\t 0.9505192637443542/0.7463701963424683\n",
      "400/574\t 0.6246465444564819/0.31641885638237\n",
      "500/574\t 0.6681424975395203/0.5754125714302063\n",
      "0.6840086410876116\n",
      "0/574\t 0.8549034595489502/0.5854130387306213\n",
      "100/574\t 0.8255133628845215/0.7331435680389404\n",
      "200/574\t 0.7898080945014954/0.2889920473098755\n",
      "300/574\t 0.8434247970581055/0.45008161664009094\n",
      "400/574\t 0.8021948337554932/0.2798524498939514\n",
      "500/574\t 0.7587068676948547/0.4931146502494812\n",
      "0.6508517459198844\n",
      "0/574\t 0.7204117774963379/0.3965621590614319\n",
      "100/574\t 0.6938701272010803/0.4912928342819214\n",
      "200/574\t 0.6996135115623474/0.2724113464355469\n",
      "300/574\t 0.8874189257621765/0.713820219039917\n",
      "400/574\t 0.9853348135948181/0.7137844562530518\n",
      "500/574\t 0.7225504517555237/0.6242028474807739\n",
      "0.6748701525912659\n",
      "0/574\t 0.909372866153717/0.5621765851974487\n",
      "100/574\t 0.7423672676086426/0.5757299661636353\n",
      "200/574\t 0.810258150100708/0.5679926872253418\n",
      "300/574\t 0.9293273091316223/0.46398961544036865\n",
      "400/574\t 1.165002703666687/0.3870283365249634\n",
      "500/574\t 0.7951234579086304/0.27622365951538086\n",
      "0.6394296238787934\n"
     ]
    }
   ],
   "source": [
    "for _ in range(5):\n",
    "    batch_idx = 0\n",
    "    model.train()\n",
    "    for batch_cate, batch_emb, batch_y in train_loader:\n",
    "        for key in category_dict.keys():\n",
    "            batch_cate[key] = batch_cate[key].long().to(device)\n",
    "\n",
    "        batch_y = batch_y.float().to(device)\n",
    "        batch_emb = batch_emb.to(device)\n",
    "        pred = model(batch_cate, batch_emb).view(-1)\n",
    "        # loss = loss_fn()\n",
    "        pos_weight=torch.ones([len(batch_y)]) * 100\n",
    "        pos_weight = pos_weight.to(device)\n",
    "        loss = F.binary_cross_entropy_with_logits(pred, batch_y, \n",
    "                                           pos_weight = pos_weight)\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        if batch_idx % 100 == 0:\n",
    "            print(\"{0}/{1}\\t {2}/{3}\".format(\n",
    "                batch_idx, len(train_loader), loss.item(), \n",
    "                auc(batch_y, torch.sigmoid(pred), reorder=True).item()\n",
    "            ))\n",
    "\n",
    "        batch_idx += 1\n",
    "\n",
    "    with torch.no_grad():\n",
    "        model.eval()\n",
    "        val_pred = []\n",
    "        for batch_cate, batch_emb, batch_y in val_loader:\n",
    "            for key in category_dict.keys():\n",
    "                batch_cate[key] = batch_cate[key].long().to(device)\n",
    "            batch_emb = batch_emb.to(device)\n",
    "            pred = model(batch_cate, batch_emb).view(-1)\n",
    "            pred = torch.sigmoid(pred).data.cpu().numpy()\n",
    "            val_pred.append(pred)      \n",
    "    print(roc_auc_score(val_loader.dataset.label.astype(int), np.hstack(val_pred)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 368,
   "id": "0933a1a5-e620-4131-b78d-8a1b1f10a5b4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:37:27.867507Z",
     "iopub.status.busy": "2022-06-21T17:37:27.866909Z",
     "iopub.status.idle": "2022-06-21T17:38:22.836567Z",
     "shell.execute_reply": "2022-06-21T17:38:22.836017Z",
     "shell.execute_reply.started": "2022-06-21T17:37:27.867456Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "test_loader = torch.utils.data.DataLoader(\n",
    "    MLPDataset(\n",
    "        test_df.iloc[:, :],\n",
    "        test_data,\n",
    "        None, False\n",
    "    ),\n",
    "    batch_size=5000,\n",
    "    shuffle=False,\n",
    "    num_workers=0,\n",
    ")\n",
    "\n",
    "with torch.no_grad():\n",
    "    model.eval()\n",
    "    test_pred = []\n",
    "    for batch_cate, batch_emb in test_loader:\n",
    "        for key in category_dict.keys():\n",
    "            batch_cate[key] = batch_cate[key].long().to(device)\n",
    "        batch_emb = batch_emb.to(device)\n",
    "        pred = model(batch_cate, batch_emb).view(-1)\n",
    "        pred = torch.sigmoid(pred).data.cpu().numpy()\n",
    "        test_pred.append(pred)   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 372,
   "id": "d464ec5d-9975-4be3-ba80-ebb5c7abd963",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:38:58.866578Z",
     "iopub.status.busy": "2022-06-21T17:38:58.865988Z",
     "iopub.status.idle": "2022-06-21T17:38:58.872310Z",
     "shell.execute_reply": "2022-06-21T17:38:58.871641Z",
     "shell.execute_reply.started": "2022-06-21T17:38:58.866528Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "test_pred = np.hstack(test_pred)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 373,
   "id": "2b3831e0-b486-42b2-affe-3583c8c57d6f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-06-21T17:38:59.870464Z",
     "iopub.status.busy": "2022-06-21T17:38:59.869870Z",
     "iopub.status.idle": "2022-06-21T17:39:00.225673Z",
     "shell.execute_reply": "2022-06-21T17:39:00.224928Z",
     "shell.execute_reply.started": "2022-06-21T17:38:59.870415Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "pd.DataFrame({'predict': (test_pred > 0.2).astype(int)}).to_csv('sub.csv', index=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "08172aa9-f7cc-476a-abe8-8799bd473720",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.6 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
